package com.apes.fico.finance.model.gl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.apes.fico.engine.model.VoucherTemplate;
import com.apes.fico.finance.respository.GLVoucherPoolRepository;
import com.apes.fico.masterdata.gl.model.AccountingOperatingChart;
import com.apes.fico.masterdata.gl.model.AccountingVoucherType;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.DefaultPersistListener;
import com.apes.framework.jpa.Model;
import com.apes.framework.jpa.Name;
import com.apes.framework.jpa.comment.Comment;
import com.apes.framework.jpa.generator.ApesGenerator;
import com.apes.framework.plugin.soe.annotations.State;
import com.apes.framework.plugin.soe.annotations.States;
import com.apes.framework.util.ApesConst;
import com.apes.framework.util.Arith;
import com.apes.framework.util.DateUtil;
import com.apes.scm.masterdata.party.model.PartyRole;
import com.apes.scm.masterdata.party.repository.PartyRoleRepository;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.SelectBeforeUpdate;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @program: apestech
 * @description: 财务凭证
 * @author: Chengxm
 * @create: 2018-12-24 13:53
 **/
@Getter
@Setter
@SelectBeforeUpdate
@DynamicInsert
@DynamicUpdate
@Entity
@EntityListeners(DefaultPersistListener.class)
@Table(name = GLVoucher.CONST_TABLE_NAME, indexes = {
        @Index(columnList = "voucher_no,period,year,company_id,handwork_flag", unique = true),
        @Index(columnList = "company_id,voucher_template_id,period")
})
public class GLVoucher extends Model {
    public final static String CONST_TABLE_NAME = "gl_voucher";

    @Id
    @Column(name = "id", nullable = false, length = 120)
    @ApesGenerator(code = CONST_TABLE_NAME, name = "财务凭证", padding = 5, prefix = "GL%(yy)%(month)", useDateRange = true)
    @Comment("凭证内码")
    private String id;

    @NotNull
    @Column
    @Comment("会计期间")
    private String period;

    @NotNull
    @Column
    @Comment("会计年度")
    private int year;

    @NotNull
    @Column(name = "year_month")
    @Comment("年月")
    private int yearMonth;

    @NotNull
    @Column(name = "month")
    @Comment("月")
    private int month;

    @NotNull
    @JSONField(format = DateUtil.SHOW_DATE_FORMAT)
    @Temporal(TemporalType.DATE)
    @Column(name = "post_date")
    @Comment("记账日期")
    private Date postDate;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "company_id", referencedColumnName = "id", nullable = false)
    @Comment("公司")
    private PartyRole company;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "acc_voucher_type_id", referencedColumnName = "id", nullable = false)
    @Comment("凭证类型")
    private AccountingVoucherType accountingVoucherType;

    @OneToMany(mappedBy = "voucher", cascade = CascadeType.ALL, orphanRemoval = true)
    @OrderBy("orderNum asc")
    private List<GLVoucherItem> voucherItemList = new ArrayList(); //凭证分录

    public List<GLVoucherItem> getVoucherItemList() {
        if (voucherItemList == null) voucherItemList = new ArrayList<>();
        for (GLVoucherItem item : voucherItemList) {
            item.setVoucher(this);
        }
        return voucherItemList;
    }

    @Column(name = "debit_total", columnDefinition = ApesConst.FIELD_MONEY_16_4_0, nullable = false)
    @Comment("借方合计")
    private double debitTotal = 0;

    @Column(name = "credit_total", columnDefinition = ApesConst.FIELD_MONEY_16_4_0, nullable = false)
    @Comment("贷方合计")
    private double creditTotal = 0;


    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "approve_uid", referencedColumnName = "id")
    @Comment("审核人")
    private PartyRole approveUID;

    @Column(name = "approve_date")
    @Comment("审核日期")
    private Date approveDate;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "reversal_uid", referencedColumnName = "id")
    @Comment("冲红人")
    private PartyRole reversalUID;

    @Column(name = "reversal_date")
    @Comment("冲红日期")
    private Date reversalDate;

    @Column(name = "reversal_voucher_id")
    @Comment("冲红凭证")
    private String reversalVoucher;

    @Column(name = "temp_flag", nullable = false, columnDefinition = "number(1,0) default 1")
    @Comment("暂存标记")
    private boolean tempFlag = false;

    @Name
    @Column(name = "remark", length = 200)
    @Comment("备注")
    private String remark;

    @Column(name = "attachment", columnDefinition = ApesConst.FIELD_QTY_16_4)
    @Comment("附件数")
    private int attachment;

    @States(name = "状态", values = {
            @State(id = "sent", name = "创建"),
            @State(id = "approve", name = "已审核"),
            @State(id = "reversal", name = "冲红"),
    })
    @Column(name = "state")
    @Comment("状态(create:创建,approve:审核)")
    private String state;

    @Column(name = "voucher_no")
    @Comment("凭证号")
    private int voucherNo = 1;

    @Column(name = "handwork_flag", nullable = false, columnDefinition = "number(1,0) default 1")
    @Comment("手工标记")
    private boolean handworkFlag = true;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "voucher_template_id", referencedColumnName = "id")
    @Comment("凭证模板")
    private VoucherTemplate voucherTemplate;

    @Column(name = "business_type", length = 64)
    @Comment("业务凭证类型")
    private String businessType;

    @Column(name = "business_id", length = 32)
    @Comment("业务凭证编号")
    private String businessId;

    @Transient
    public Boolean cancelCarryOverPL = false;


    @Transient
    public JSONObject creatorName;

    public void setCreatorName(JSONObject creatorName){
        return;
    }

    public JSONObject getCreatorName(){
        if (getCreator()==null)return null;
        PartyRole partyRole = SpringManager.getBean(PartyRoleRepository.class).findOne(getCreator());
        JSONObject resultJson = new JSONObject();
        resultJson.put("id",partyRole.getId());
        resultJson.put("name",partyRole.getName());
        return resultJson;

    }
    public Boolean getCancelCarryOverPL() {
        return cancelCarryOverPL;
    }

    public void setCancelCarryOverPL(Boolean cancelCarryOverPL) {
        this.cancelCarryOverPL = cancelCarryOverPL;
    }

    public void checkVoucherNo() {
        GLVoucherPoolRepository repository = SpringManager.getBean(GLVoucherPoolRepository.class);
        GLVoucherPool cancelGlVoucherPool = repository.findFirstByCompanyAndPeriodAndYearAndHandworkFlagAndCancelOrderByVoucherNoAsc(this.company, this.period, this.year, this.handworkFlag, 1);
        if (cancelGlVoucherPool != null) {
            voucherNo = cancelGlVoucherPool.getVoucherNo();
            cancelGlVoucherPool.setCancel(0);
            cancelGlVoucherPool.setHandworkFlag(this.handworkFlag);
            repository.saveAndFlush(cancelGlVoucherPool);
            return;
        }

        GLVoucherPool maxGLVoucherPool = repository.findFirstByCompanyAndPeriodAndYearAndHandworkFlagOrderByVoucherNoDesc(this.company, this.period, this.year, this.handworkFlag);
        if (maxGLVoucherPool != null) {
            voucherNo = maxGLVoucherPool.getVoucherNo() + 1;
            GLVoucherPool glVoucherPool = new GLVoucherPool();
            glVoucherPool.setCompany(company);
            glVoucherPool.setPeriod(period);
            glVoucherPool.setYear(year);
            glVoucherPool.setVoucherNo(voucherNo);
            glVoucherPool.setHandworkFlag(this.handworkFlag);
            repository.saveAndFlush(glVoucherPool);
            return;
        }

        GLVoucherPool glVoucherPool = new GLVoucherPool();
        glVoucherPool.setCompany(company);
        glVoucherPool.setPeriod(period);
        glVoucherPool.setYear(year);
        glVoucherPool.setVoucherNo(voucherNo);
        glVoucherPool.setHandworkFlag(this.handworkFlag);
        repository.saveAndFlush(glVoucherPool);
    }

    /**
     * 赋值合计并检查借贷
     */
    public void checkDebitAndCredit() {
        double debit = 0;
        double credit = 0;
        Boolean invalidData = false;
        for (int i = 0; i < voucherItemList.size(); i++) {
            debit += voucherItemList.get(i).getDebit();
            credit += voucherItemList.get(i).getCredit();
            AccountingOperatingChart accountingOperatingChart = voucherItemList.get(i).getAccountingOperatingChart();
            String chartCompanyId = accountingOperatingChart.getCompany().getId();
            if (voucherItemList.get(i).getDebit() == 0 && voucherItemList.get(i).getCredit() == 0) {
                if (handworkFlag) {
                    throw new RuntimeException("无效录入：分录借贷不能同时为0!");
                } else {
                    invalidData = true;
                }
            }
            if (voucherItemList.get(i).getDebit() != 0 && voucherItemList.get(i).getCredit() != 0) {
                if (handworkFlag) {
                    throw new RuntimeException("无效录入：分录借贷不能同时有值!");
                } else {
                    invalidData = true;
                }
            }
            if (!Objects.equals(this.company.getId(),chartCompanyId)){
                throw new RuntimeException(String.format("第 %s 行 %s %s科目编码非 %s 的运营科目编码，请检查！！！",i+1,accountingOperatingChart.getId(),accountingOperatingChart.getName(),company.getName()));
            }
        }
        if (invalidData)
            voucherItemList = voucherItemList.stream().filter((item -> !(item.getDebit() == 0 && item.getCredit() == 0))).collect(Collectors.toList());
        if (Arith.compare(Arith.round(debit,4), Arith.round(credit,4), 3) != 0) throw new RuntimeException("录入借贷不平!");
        this.debitTotal = debit;
        this.creditTotal = credit;
    }
}
